#include "zen_game_graphics.h"
#include "zen_game_activity.h"
#include "zen_game_scene.h"
#include "zen_game_sprite.h"
#include "zen_game_label.h"
#include "zen_game_action.h"
#include "zen_game_texture.h"
#include "zen_game_dust.h"
#include "zen_game_digit.h"
#include "zen_utils.h"
#include "zen_random.h"

using namespace Zen::Game;
using namespace Zen;

bool Linked(int value)
{
    bool lt = value & (1 << 1);
    bool lb = value & (1 << 4);
    bool rt = value & (1 << 2);
    bool rb = value & (1 << 5);
    bool t = value & 1;
    bool b = value & (1 << 6);
    bool m = value & (1 << 3);

    if (m)
    {
        if (t && !lt && !rt) return false;
        if (b && !lb && !rb) return false;
        return true;
    }
    if (t && b)
    {
        return (lt && lb) || (rt && rb);
    }
    if (t)
    {
        if (lb && !lt) return false;
        if (rb && !rt) return false;
        return true;
    }
    if (b)
    {
        if (lt && !lb) return false;
        if (rt && !rb) return false;
        return true;
    }
    return !((lt || lb) && (rt || rb));
}

class MainGame : public Zen::Game::ActivityDelegate
{
    std::shared_ptr<Zen::Game::Scene> scene;
    std::shared_ptr<TextureCache> imageCache;
    std::shared_ptr<Zen::FontBrush> mFontBrush;

    std::shared_ptr<TextureFrame> mCatFrame;
    std::shared_ptr<Zen::Game::Digit> mDigit;

    Zen::Point2 mViewSize{};

  public:
    void AddAllDigit()
    {
        float sz = mViewSize.x / (32 * 3.2f);

        int v = 0;
        Point2 pos{ sz, sz };
        for (int i = 0; i < 32; ++i)
        {
            pos.y += sz * 5;
            pos.x = sz;
            for (int j = 0; j < 32; ++j)
            {
                pos.x += 3 * sz;
                auto d = scene->CreateInnerNode<Digit>();
                d->SetSize({ 2 * sz, 4 * sz });
                d->SetAnchor({ 0.5f, 0.5f });
                d->SetPosition(pos);
                if (Linked(v))
                {
                    d->SetColor(0xFFFF00FF);
                }
                d->SetBitValue(v++);
                if (v >= (1 << 9)) return;
            }
        }
    }

    virtual void OnLaunch() override
    {
        mViewSize = Activity::Only()->GetScreenSize();
        Zen::Game::Activity::Only()->GetGraphicsEngine()->SetClearColor(Zen::Color4f(0x336699ff));
        scene = Zen::Game::Activity::Only()->GetRoot()->CreateInnerNode<Scene>();
        scene->GetCamera()->Orthogonal(mViewSize, { 0, 0 });

        this->LoadFont();
        this->LoadTexture();
        this->AddFPS();

        this->AddSprite();
        this->AddFontLable();
        this->AddDust();
        this->AddDigit();
        this->AddAllDigit();
        this->RunCatAction();
        this->AddTileLabel();
        return;
    }
    void RunCatAction()
    {
#if 1
        auto action = ActionCall::Generate(
          [this](float, int) {
              if (Activity::Only()->GetRealFPS() > 50)
              {
                  this->AddCat();
              }
              return true;
          },
          0, 2, 0.2f);
        Activity::Only()->GetActionDispather()->RunAction(action, scene);
#endif
    }
    void LoadFont()
    {
        auto data = Zen::Utils::Only()->LoadResource("src/Inkfree.ttf");
        if (data.empty()) throw "error ttf file";
        auto font = Zen::Font::Generate(data);
        auto brush = Zen::FontBrush::Generate(font, 64);
        mFontBrush = brush;
    }
    void LoadTexture()
    {
        imageCache = std::make_shared<TextureCache>(Zen::Game::Activity::Only()->GetGraphicsEngine());
        imageCache->AddImage("image0", BuiltinImages::Only()->CreateCircleTexture(Zen::EPixel::RGBA, 100));
        imageCache->AddImage("image1", BuiltinImages::Only()->CreateParticleTexture(Zen::EPixel::RGBA, 100));
        mCatFrame = std::make_shared<TextureFrame>();
        mCatFrame->SetTexture(imageCache->GetShared("src/cat.png"));
        imageCache->GetShared("src/1.png");
    }

    void AddDigit()
    {
        auto d = scene->CreateInnerNode<Zen::Game::Digit>();
        d->SetSize({ 100, 100 });
        d->SetAnchor({ 0.5f, 0.5f });
        d->SetPosition(mViewSize / 2);
        d->SetSiblingOrder(10);
        auto action = ActionCall::Generate(
          [d, this](float, int) {
              d->SetBitValue(d->GetBitValue() + 1);
              return true;
          },
          0, 2, 0.05f);
        Activity::Only()->GetActionDispather()->RunAction(action, scene);
    }
    void AddDust()
    {
        GravityDustConfig config;
        //TODO:set config.
        auto gde = std::make_shared<GravityDustEmitor>(config);
        auto dust = scene->CreateInnerNode<Dust>();
        dust->SetBlend(Zen::Graphics::EBlend::Addition);
        dust->SetTexture(imageCache->GetShared("image1"));
        dust->SetEmitor(gde);
        dust->SetPosition(mViewSize / 2);
        dust->SetDustCount(100);
        dust->RestartEmit();
    }
    void AddSprite()
    {
        auto sprite = scene->CreateInnerNode<Sprite>();
        sprite->SetTexture(imageCache->GetShared("image0"));
        sprite->SetSize({ 100, 100 });
        sprite->SetAnchor({ 0, 0 });
        sprite->SetPosition({ 0, 0 });
    }
    void AddCat()
    {
        auto sprite = scene->CreateInnerNode<Sprite>();
        sprite->SetTextureFrame(mCatFrame);
        sprite->SetSize({ mR.NextF<float>(100), mR.NextF<float>(100) });
        sprite->SetAnchor({ 0.5, 0.5 });
        sprite->SetPosition({ 0, 0 });
        auto action = ActionCall::Generate(
          [sprite, this](float, int) {
              sprite->SetPosition(RandomMove(sprite->GetPosition()));
              return true;
          },
          0, 0, 0);
        Activity::Only()->GetActionDispather()->RunAction(action, sprite);
    }
    void AddFontLable()
    {
        auto s = "Hello darkness my old friend.I've come to talk with you again.\nBecause a vision softly creeping.\nLeft its seeds while I was sleeping.";
        auto lb = scene->CreateInnerNode<TextLabel>();
        lb->SetTextRender(std::make_shared<DefaultTextRender>(mFontBrush));
        Zen::FontStyle style;
        style.Alignment = 0.5f;
        style.WidthLimit = 0;
        style.WordSpace = 0;
        style.UnderLineSize = 2;
        style.UnderLineShift = 5;
        lb->SetText(s);
        lb->SetAnchor({ 0, 1 });
        lb->SetFontStyle(style);
        lb->SetPosition({ 0, mViewSize.y });
        lb->SetFontSize(32);
    }
    void AddFPS()
    {
        auto textRender = std::make_shared<DefaultTextRender>(mFontBrush);

        auto lb = scene->CreateInnerNode<TextLabel>();
        lb->SetTextRender(textRender);
        lb->SetText("FPS:");
        lb->SetAnchor({ 1, 0 });
        lb->SetPosition({ mViewSize.x, 0 });
        lb->SetFontSize(32);
        auto action = ActionCall::Generate(
          [lb](float, int) {
              lb->SetText("FPS:" + std::to_string(Activity::Only()->GetRealFPS()));
              return true;
          },
          0, 0, 1);
        Activity::Only()->GetActionDispather()->RunAction(action, lb);
    }
    Zen::Point2 RandomMove(Zen::Point2 p)
    {
        float xR = 0.4f + (p.x / mViewSize.x) * 0.2f;
        float yR = 0.4f + (p.y / mViewSize.y) * 0.2f;
        p.x += (mR.NextF<float>() - xR) * 15.f;
        p.y += (mR.NextF<float>() - yR) * 15.f;
        return p;
    }
    void AddTileLabel()
    {
        auto tileMap = TileMap::Generate('a', 4, 1);
        auto info = tileMap->GetCharactor('a');
        auto v = *info;
        v.Move.y = 0.5;
        //v.Move.x = 0.25f;
        v.Size = { 0.5f, 0.5f };
        tileMap->AddCharactor(v);
        auto label = scene->CreateInnerNode<TileLabel>();
        label->SetTileMap(tileMap);
        label->SetText("ab\n c d");
        label->SetTileSize({ 30.f, 50.f });
        label->SetPosition(mViewSize / 2);
        label->SetAnchor({ 0.5f, 0.5f });
        label->SetTexture(imageCache->GetShared("src/1.png"));
    }

    virtual void OnResize() override {}

    virtual void OnPause() override {}

    virtual void OnResume() override {}

    virtual void OnExit() override {}

    virtual void OnEvent(std::string event) override {}
    Zen::Random mR;
};

std::shared_ptr<Zen::AppDelegate> ZenAppMain(Zen::AppConfig* config)
{
    config->WindowHeight = 800;
    config->WindowWidth = 800;
    config->FramesPerSecond = 60.f;
    config->Title = "I";

    Zen::Game::Activity::Only()->InitGraphicsEngine(Zen::Graphics::Engine::GLES());
    Zen::Game::Activity::Only()->InitDelegate(std::make_shared<MainGame>());
    return Zen::Game::Activity::Only();
}
